<!-- eslint-disable max-lines -->
<!-- eslint-disable @typescript-eslint/no-floating-promises -->
<!-- eslint-disable complexity -->
<script setup lang="ts">
  import { nextTick, ref, computed } from 'vue'
  import { useChat } from '../hooks/useChat'
  import { extractFirstPathContent, getFileType } from '../utils'
  import type { ChatItem } from '../assets/types'
  import { Bubble } from 'vue-element-plus-x'
  import SvgIcon from '@/components/SvgIcon/index.vue'
  import { NButton, useMessage } from 'naive-ui'
  import { copyToClip } from '@/utils/copy'
  import { cloneDeep } from 'lodash'

  // 组件属性定义
  const props = defineProps<{
    chatApi: string // 聊天API地址
    apiKey: string // API密钥
    user: string // 用户标识
    fullScreen: boolean // 是否全屏显示
    agent: string // 代理类型
  }>()

  // 特殊路径和ID信息，用于导航和处理特定内容
  const menuPath = ref('')
  const nodeId = ref('')
  const cameraId = ref('')
  const dzId = ref('')

  // 组件事件定义
  const emit = defineEmits(['sendMsg', 'finish', 'agentNav'])

  // 聊天记录和状态管理
  const chatList = ref<ChatItem[]>([]) // 聊天消息列表
  const chatItemId = ref('') // 当前聊天ID
  const curTaskId = ref('') // 当前任务ID
  const newFiles = ref<string[]>([]) // 新文件列表，用于收集AI回复中的文件

  // 初始化聊天API hooks
  const { fetchChat, cancelTask, changeDeviceId } = useChat(
    props.chatApi,
    props.apiKey,
    props.user,
    props.agent
  )

  // 获取上一条消息ID，用于构建对话上下文
  const getParentMessageId = computed(() => {
    return chatList.value.length > 0 ? chatList.value[chatList.value.length - 1].id : undefined
  })

  // 更新设备ID
  const _changeDeviceId = (id: string) => {
    changeDeviceId(id)
  }

  /**
   * 开始聊天会话
   * @param message 用户输入的消息
   * @param index 如果是刷新现有消息，则为消息索引；否则为-1表示新消息
   * @param deviceId 可选的设备ID
   */
  const startChat = async (message: string, index = -1, deviceId?: string) => {
    // 获取父消息ID，用于保持对话上下文
    const parent_message_id = getParentMessageId.value

    // 创建用户消息对象
    const chatItem: ChatItem = {
      conversation_id: chatItemId.value,
      id: '',
      content: message,
      isUser: true,
      loading: false,
      deviceId,
      fileList: [],
      processing: false,
    }

    // 对于新消息，添加到聊天列表
    if (index === -1) chatList.value.push(chatItem)

    // 通知父组件用户已发送消息
    emit('sendMsg')

    // 创建AI响应消息对象
    const responseItem: ChatItem = {
      conversation_id: chatItemId.value,
      id: '',
      content: '',
      isUser: false,
      loading: true,
      task_id: '',
      fileList: [],
      processing: true,
    }

    // 根据是新消息还是刷新消息决定添加或替换
    if (index === -1) chatList.value.push(responseItem)
    else chatList.value.splice(index, 1, responseItem)

    // 滚动到页面底部显示最新消息
    scrollToBottom()

    // 调用聊天API获取响应
    await fetchChat(
      message,
      responseItem.conversation_id,
      ({ answer, event, conversation_id, message_id, task_id }) => {
        // 更新消息基础信息
        responseItem.id = message_id
        chatItemId.value = conversation_id
        responseItem.conversation_id = conversation_id
        curTaskId.value = task_id
        responseItem.task_id = task_id
        if (!chatItem.conversation_id) {
          chatItem.conversation_id = conversation_id
        }

        // 根据事件类型处理消息
        if (event === 'message') {
          if (index !== -1) {
            chatList.value[index].loading = false
          } else {
            chatList.value[chatList.value.length - 1].loading = false
          }
          // 提取各种特殊标记内容，可根据智能体返回，自定义提取的内容
          const extractors = [
            {
              name: '字典',
              handler: (content: string) => {
                menuPath.value = content
              },
            },
            {
              name: '编号',
              handler: (content: string) => {
                nodeId.value = content
              },
            },
            {
              name: '相机',
              handler: (content: string) => {
                cameraId.value = content
              },
            },
            {
              name: '道闸',
              handler: (content: string) => {
                dzId.value = content
              },
            },
            {
              name: '地址',
              handler: (content: string) => {
                newFiles.value.push(content)
              },
            },
          ]

          // 处理特殊标记
          let hasSpecialContent = false
          for (const { name, handler } of extractors) {
            const content = extractFirstPathContent(answer, name)
            if (content) {
              hasSpecialContent = true
              handler(content)
            }
          }

          // 如果没有特殊标记，则作为普通文本内容处理
          if (!hasSpecialContent) {
            responseItem.content += answer
            if (index !== -1) {
              chatList.value.splice(index, 1, responseItem)
            } else {
              chatList.value[chatList.value.length - 1] = { ...responseItem }
            }
          }
        } else if (event === 'message_end') {
          // 消息结束处理
          if (index !== -1) {
            responseItem.processing = false
            const _responseItem = cloneDeep(responseItem)
            chatList.value.splice(index, 1, _responseItem)
          } else {
            chatList.value[chatList.value.length - 1].processing = false
          }
          // 发送导航事件，传递提取的路径和ID信息
          emit('agentNav', {
            path: menuPath.value,
            id: nodeId.value,
            camera: cameraId.value,
            dz: dzId.value,
          })
        } else if (event === 'error') {
          // 错误处理：移除最后一条消息并显示错误提示
          chatList.value.pop()
          responseItem.loading = false
          responseItem.processing = false
          responseItem.content += '\n请求失败，请稍后再试'
          chatList.value.push(responseItem)
        }
      },
      parent_message_id,
      deviceId
    )

    // 通知父组件消息处理完成
    emit('finish')
  }

  /**
   * 取消当前正在进行的任务
   */
  const handleCancel = async () => {
    if (!curTaskId.value) return

    // 发送取消请求
    const res = await cancelTask(curTaskId.value)
    if (res.status === 200 && res.data.result === 'success') {
      // 更新对应消息的状态
      const index = chatList.value.findIndex(item => item.task_id === curTaskId.value)
      if (index !== -1) {
        chatList.value[index].loading = false
        chatList.value[index].processing = false
        chatList.value[index].content = '已取消'
        // 创建新对象触发视图更新
        const _chatItem = cloneDeep(chatList.value[index])
        chatList.value.splice(index, 1, _chatItem)
      }
      // 通知父组件任务完成
      emit('finish')
    }
  }

  // 消息提示hook
  const message = useMessage()

  /**
   * 复制消息内容到剪贴板
   * @param content 要复制的内容
   */
  const handleCopy = async (content: string) => {
    await copyToClip(content.trim())
    message.success('复制成功')
  }

  /**
   * 刷新/重新生成回答
   * @param index 当前消息索引
   */
  const handleRefresh = async (index: number) => {
    // 忽略第一条消息或非用户消息
    if (index === 0) return
    const lastItem = chatList.value[index - 1]
    if (!lastItem.isUser) return
    // 用上一条用户消息重新请求回答
    await startChat(lastItem.content, index, lastItem.deviceId)
  }

  /**
   * 清空聊天记录和状态
   */
  const clearChat = () => {
    chatList.value = []
    chatItemId.value = ''
    curTaskId.value = ''
    menuPath.value = ''
    nodeId.value = ''
    dzId.value = ''
    cameraId.value = ''
    newFiles.value = []
  }

  // 聊天容器引用
  const containerRef = ref<HTMLElement | null>(null)

  /**
   * 滚动到聊天窗口底部
   */
  const scrollToBottom = async () => {
    await nextTick(() => {
      if (containerRef.value) {
        containerRef.value.scrollTop = containerRef.value.scrollHeight
      }
    })
  }

  /**
   * 处理文件点击，打开文件
   * @param file 文件路径
   */
  const handleFile = (file: string) => {
    window.open(file, '_blank')
  }

  /**
   * 处理消息完成事件
   * @param index 消息索引
   */
  const handleFinish = (index: number) => {
    // 滚动到底部
    scrollToBottom()
    if (chatList.value[index].processing) return
    // 处理文件列表
    if (newFiles.value.length > 0) {
      chatList.value[index].fileList = [...newFiles.value]
      newFiles.value = []
    }
  }

  // 暴露组件方法供父组件调用
  defineExpose({ startChat, chatList, handleCancel, clearChat, _changeDeviceId })
</script>

<template>
  <!-- 聊天容器 -->
  <div
    class="deepseek-chat-item h-full w-full overflow-y-auto mx-auto px-6"
    :class="{ '!w-[784px]': props.fullScreen }"
    ref="containerRef"
  >
    <!-- 聊天气泡列表 -->
    <Bubble
      class="mt-6"
      ref="bubbleRef"
      v-for="(item, index) in chatList"
      :key="item.id || index"
      @finish="handleFinish(index)"
      @writing="scrollToBottom"
      :content="item.content"
      :typing="
        !item.isUser
          ? {
              interval: 20,
              step: 6,
            }
          : false
      "
      :is-markdown="!item.isUser"
      :loading="item.loading"
      :placement="item.isUser ? 'end' : 'start'"
    >
      <!-- 用户/AI头像 -->
      <template #avatar>
        <img src="@/assets/images/avatar.png" v-if="item.isUser" alt="" />
        <img
          class="max-w-8 w-8 h-8 rounded-full border border-[#38F2F4]"
          src="@/assets/images/ai-robot.png"
          v-else
          alt=""
        />
      </template>

      <!-- 加载状态显示 -->
      <template #loading>
        <div class="flex items-center">
          <SvgIcon name="flower" class="mr-1 text-[16px] flex items-center icon-spin" />
          <div class="text-[#F5F9FF]">思考中</div>
          <SvgIcon name="loading" />
        </div>
      </template>

      <!-- 消息底部操作区 -->
      <template #footer v-if="!item.isUser && !item.loading">
        <!-- 文件列表 -->
        <div
          class="file-list flex items-center h-8 leading-8 cursor-pointer"
          @click="handleFile(o)"
          v-for="o in item.fileList"
          :key="o"
        >
          <SvgIcon :name="getFileType(o)" class="text-[24px] !flex items-center" />
          <div class="text-[#38F2F4] ml-2">{{ o.split('/').pop() }}</div>
        </div>

        <!-- 操作按钮 -->
        <div class="footer-container" v-if="!item.processing">
          <!-- 复制按钮 -->
          <NButton quaternary class="!p-0 mr-2" @click="handleCopy(item.content)">
            <template #icon>
              <SvgIcon class="text-[#F5F9FF] text-[24px] hover:text-[#38F2F4]" name="copy" />
            </template>
          </NButton>

          <!-- 刷新按钮 -->
          <NButton quaternary class="!p-0" @click="handleRefresh(index)">
            <template #icon>
              <SvgIcon class="text-[#F5F9FF] text-[24px] hover:text-[#38F2F4]" name="refresh" />
            </template>
          </NButton>
        </div>
        <div class="footer-container flex items-center" v-else>
          <SvgIcon
            name="flower"
            class="mr-1 text-[16px] text-[#38F2F4] flex items-center icon-spin"
          />
          <div class="text-[#38F2F4]">思考中</div>
          <SvgIcon name="loading" class="text-[#38F2F4]" />
        </div>
      </template>
    </Bubble>
  </div>
</template>

<style lang="less" scoped>
  .deepseek-chat-item {
    /* 旋转动画定义 */
    @keyframes spin {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    }

    /* 旋转图标样式 */
    .icon-spin {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      transform-origin: center center;
      animation: spin 1s linear infinite;
    }

    /* AI消息气泡样式 */
    :deep(.el-bubble-start) {
      .el-bubble-content-wrapper {
        .el-bubble-content {
          background-color: transparent;
          color: #f5f9ff;
          padding: 7px 15px 7px 9px;
        }
      }
    }

    /* 用户消息气泡样式 */
    :deep(.el-bubble-end) {
      .el-bubble-content-wrapper {
        .el-bubble-content {
          padding: 7px 9px 7px 15px;
          background-color: #192646;
          color: #f5f9ff;
          border: 1px solid rgba(255, 255, 255, 0.1);
        }
      }
    }

    /* 代码块样式 */
    :deep(pre) {
      background-color: #19264660;
      color: #f5f9ff;
      border: 1px solid rgba(255, 255, 255, 0.1);
      border-radius: 4px;
      white-space: break-spaces;
      padding: 7px 15px 7px 9px;
      margin: 10px 0;

      code {
        font-family:
          v-sans,
          system-ui,
          -apple-system,
          BlinkMacSystemFont,
          'Segoe UI',
          sans-serif,
          'Apple Color Emoji',
          'Segoe UI Emoji',
          'Segoe UI Symbol';
      }
    }
  }
</style>
